/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */
package com.pig4cloud.pigx.user.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.common.customize.enums.CommonDictEnum;
import com.pig4cloud.pigx.common.customize.mysql.utils.MysqlUtil;
import com.pig4cloud.pigx.common.customize.security.utils.SecurityUtil;
import com.pig4cloud.pigx.common.customize.utils.CommonUtil;
import com.pig4cloud.pigx.common.security.service.PigxUser;
import com.pig4cloud.pigx.user.dto.UserApiIpLockDTO;
import com.pig4cloud.pigx.user.entity.UserApiIp;
import com.pig4cloud.pigx.user.entity.UserApiIpLockDetail;
import com.pig4cloud.pigx.user.enums.UserApiIpEnum;
import com.pig4cloud.pigx.user.enums.UserApiIpLockDetailEnum;
import com.pig4cloud.pigx.user.mapper.UserApiIpLockDetailMapper;
import com.pig4cloud.pigx.user.mapper.UserApiIpMapper;
import com.pig4cloud.pigx.user.mapstruct.UserApiIpStruct;
import com.pig4cloud.pigx.user.service.UserApiIpService;
import com.pig4cloud.pigx.user.util.UserUtil;
import com.pig4cloud.pigx.user.vo.UserApiIpCacheVO;
import com.pig4cloud.pigx.user.vo.UserApiIpVO;
import lombok.AllArgsConstructor;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;

/**
 * 用户apiKey ip
 *
 * @since 2022-10-28 15:47:40
 */
@Service
@AllArgsConstructor
public class UserApiIpServiceImpl extends ServiceImpl<UserApiIpMapper, UserApiIp> implements UserApiIpService {

	private final CommonUtil commonUtil;
	private final UserUtil userUtil;
	private final SecurityUtil securityUtil;
	private final UserApiIpLockDetailMapper userApiIpLockDetailMapper;

	/**
	 * 分页查询
	 *
	 * @param page      分页条件
	 * @param condition 查询条件
	 * @return {@link Page< UserApiIpVO>}
	 * @since 2022/10/28 17:41
	 */
	@Override
	public Page<UserApiIpVO> page(Page<UserApiIp> page, UserApiIp condition) {
		QueryWrapper<UserApiIp> query = new QueryWrapper<>();
		query.lambda()
				.eq(!Objects.isNull(condition.getApiKeyId()), UserApiIp::getApiKeyId, condition.getApiKeyId())
				.eq(Strings.isNotBlank(condition.getAddress()), UserApiIp::getAddress, condition.getAddress())
				.eq(Strings.isNotBlank(condition.getStatus()), UserApiIp::getStatus, condition.getStatus())
				.eq(UserApiIp::getIsDeleted, CommonDictEnum.IsDeleted.NO.getValue())
				.orderByDesc(UserApiIp::getLockCount);

		page = baseMapper.selectPage(page, query);

		// 转换为vo类
		List<UserApiIpVO> voList = UserApiIpStruct.INSTANCE.list2VOList(page.getRecords());
		// 遍历，翻译字典项
		voList.forEach(vo -> {
			vo.setStatus(UserApiIpEnum.Status.findName(vo.getStatus()));
		});

		return MysqlUtil.transferPage(page, voList);
	}

	/**
	 * 人工封禁
	 *
	 * @param dto dto
	 * @since 2022/10/31 11:43
	 */
	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void lockManual(UserApiIpLockDTO dto) {
		// 获取当前用户
		PigxUser user = SecurityUtil.getLoginUserNotNull();
		LocalDateTime now = LocalDateTime.now();

		// 查询并锁定ip
		QueryWrapper<UserApiIp> query = new QueryWrapper<>();
		query.lambda()
				.eq(UserApiIp::getId, dto.getId())
				.eq(UserApiIp::getStatus, UserApiIpEnum.Status.NORMAL.getValue())
				.last("for update");
		UserApiIp ip = baseMapper.selectOne(query);
		if (Objects.isNull(ip)) {
			throw new RuntimeException("Ip地址状态已发生改变");
		}

		// 初始化封禁记录信息
		UserApiIpLockDetail detail = new UserApiIpLockDetail();
		detail.setIpId(dto.getId());
		detail.setType(UserApiIpLockDetailEnum.Type.LOCK.getValue());
		detail.setOperationType(UserApiIpLockDetailEnum.OperationType.MANUAL.getValue());
		detail.setCurrentLockCount(ip.getCurrentLockCount());
		detail.setLockDuration(-1);
		detail.setLockReason(dto.getRemark());
		detail.setCreateName(user.getName());
		securityUtil.initCreateAbout(detail, user, now);

		// 修改ip信息
		UpdateWrapper<UserApiIp> updateIp = new UpdateWrapper<>();
		updateIp.lambda()
				.setSql("lock_count = lock_count + 1")
				.set(UserApiIp::getLastLockTime, now)
				.set(UserApiIp::getLastLockDuration, -1)
				.set(UserApiIp::getLockId, detail.getId())
				.set(UserApiIp::getUpdateTime, now)
				.set(UserApiIp::getUpdateBy, user.getId())
				.set(UserApiIp::getStatus, UserApiIpEnum.Status.FOREVER_LOCK.getValue())
				.eq(UserApiIp::getId, dto.getId());
		if (baseMapper.update(null, updateIp) != 1) {
			throw new RuntimeException("操作失败");
		}

		// 持久化封禁记录
		if (userApiIpLockDetailMapper.insert(detail) != 1) {
			throw new RuntimeException("操作失败");
		}

		// 更新ApiIp缓存
		userUtil.flushRedisApiIp(ip.getApiKeyId(), null);
	}

	/**
	 * 人工解封
	 *
	 * @param dto dto
	 * @since 2022/10/31 13:58
	 */
	@Override
	@Transactional(rollbackFor = Throwable.class)
	public void unlockManual(UserApiIpLockDTO dto) {
		// 获取当前用户
		PigxUser user = SecurityUtil.getLoginUserNotNull();
		LocalDateTime now = LocalDateTime.now();

		// 查询并锁定ip
		QueryWrapper<UserApiIp> query = new QueryWrapper<>();
		query.lambda()
				.eq(UserApiIp::getId, dto.getId())
				.in(UserApiIp::getStatus, UserApiIpEnum.Status.LOCK.getValue(), UserApiIpEnum.Status.FOREVER_LOCK.getValue())
				.last("for update");
		UserApiIp ip = baseMapper.selectOne(query);
		if (Objects.isNull(ip)) {
			throw new RuntimeException("Ip地址状态已发生改变");
		}

		// 封禁记录id为空
		if (Objects.isNull(ip.getLockId())) {
			throw new RuntimeException("数据异常，未能查询到封禁记录");
		}
		// 查询封禁记录
		UserApiIpLockDetail lockDetail = userApiIpLockDetailMapper.selectById(ip.getLockId());
		// 封禁记录为空
		if (Objects.isNull(lockDetail)) {
			throw new RuntimeException("数据异常，未能查询到封禁记录");
		}

		// 初始化解封记录信息
		UserApiIpLockDetail detail = new UserApiIpLockDetail();
		detail.setIpId(dto.getId());
		detail.setType(UserApiIpLockDetailEnum.Type.UNLOCK.getValue());
		detail.setOperationType(UserApiIpLockDetailEnum.OperationType.MANUAL.getValue());
		detail.setLockId(lockDetail.getId());
		detail.setLockReason(lockDetail.getLockReason());
		detail.setUnlockReason(dto.getRemark());
		detail.setCreateName(user.getName());
		securityUtil.initCreateAbout(detail, user, now);

		// 修改ip信息
		UpdateWrapper<UserApiIp> updateIp = new UpdateWrapper<>();
		updateIp.lambda()
				.set(UserApiIp::getLockId, null)
				.setSql("manual_unlock_count = manual_unlock_count + 1")
				.set(UserApiIp::getStatus, UserApiIpEnum.Status.NORMAL.getValue())
				.set(UserApiIp::getUnlockTime, now)
				.set(UserApiIp::getUpdateTime, now)
				.set(UserApiIp::getUpdateBy, user.getId())
				.eq(UserApiIp::getId, dto.getId());
		if (baseMapper.update(null, updateIp) != 1) {
			throw new RuntimeException("操作失败");
		}

		// 持久化解封记录
		if (userApiIpLockDetailMapper.insert(detail) != 1) {
			throw new RuntimeException("操作失败");
		}

		// 更新ApiIp缓存
		userUtil.flushRedisApiIp(ip.getApiKeyId(), null);
	}

	/**
	 * 根据AccessKey查询相关IP信息
	 *
	 * @param apiKeyId  apiKeyId
	 * @param accessKey accessKey
	 * @return {@link List< UserApiIpCacheVO>}
	 * @since 2022/11/9 10:53
	 */
	@Override
	public List<UserApiIpCacheVO> getCache(Long apiKeyId, String accessKey) {
		return userUtil.getApiKeyIpCache(apiKeyId, accessKey);
	}
}
